Skip to content

fix(agent-context): support multiple context files safely#2969

Open
AustinZ21 wants to merge 3 commits into
github:mainfrom
AustinZ21:fix/agent-context-multiple-files
Open

fix(agent-context): support multiple context files safely#2969
AustinZ21 wants to merge 3 commits into
github:mainfrom
AustinZ21:fix/agent-context-multiple-files

Conversation

@AustinZ21

Copy link
Copy Markdown

Summary

This PR lets the bundled agent-context extension manage more than one coding-agent context file from the same Spec Kit run.

The motivating workflow is a repo that uses Claude Code and Codex interchangeably and wants Spec Kit's generated context block synced to both CLAUDE.md and AGENTS.md. The implementation keeps the existing singular context_file behavior, while adding an optional context_files list for projects that intentionally keep multiple agent anchors in sync.

What Changed

  • Added optional context_files support to the agent-context config template.
  • Updated the bash and PowerShell update scripts to refresh every configured context file.
  • Updated Python integration setup/removal paths to upsert and remove the managed block across configured files.
  • Rendered command templates with a readable multi-file context target when context_files is configured.
  • Preserved existing non-empty context_files during integration switches.
  • Added project-root containment validation for context file paths:
    • rejects absolute paths
    • rejects Windows drive paths
    • rejects backslash separators
    • rejects .. path segments
    • confirms the resolved target stays inside the project root
  • Kept specify extension disable agent-context as a full opt-out: disabled projects skip upsert/removal and ignore stale context_files during command rendering.
  • Updated agent-context docs and command help to describe multi-file configuration and safety behavior.

Why

Today Spec Kit stores one managed context block target in context_file. That works well for a single coding agent, but mixed-agent projects can need more than one context anchor. For example, Codex reads AGENTS.md, while Claude-oriented setups often use CLAUDE.md. Without first-class multi-file support, teams have to choose one anchor or maintain duplicate context manually.

This change makes the multi-agent case explicit and configurable without hard-coding any specific agent names or file paths.

Safety Notes

The script and Python integration paths now enforce matching path constraints so a configured context file cannot escape the project root. The disabled-extension path also avoids validating stale config before the opt-out gate, so disabling agent-context remains a complete opt-out even if old config contains invalid paths.

Validation

  • bash -n extensions/agent-context/scripts/bash/update-agent-context.sh
  • python -m compileall -q src\specify_cli
  • pytest tests/extensions/test_extension_agent_context.py -q
    • 50 passed
  • pytest tests/integrations/test_integration_codex.py::TestCodexInitFlow::test_plan_skill_references_configured_context_files tests/integrations/test_integration_codex.py::TestCodexInitFlow::test_plan_skill_ignores_context_files_when_agent_context_disabled -q
    • 2 passed
  • pytest tests/test_agent_config_consistency.py -q
    • 28 passed
  • specify --help
  • Manual PowerShell smoke test:
    • configured context_files: [AGENTS.md, CLAUDE.md]
    • ran update-agent-context.ps1 specs/123-test/plan.md
    • verified both files were updated
  • Manual bash smoke test:
    • configured context_files: [AGENTS.md, CLAUDE.md]
    • ran update-agent-context.sh specs/123-test/plan.md
    • verified both files were updated

Known Existing Test Failure

The broader Codex integration slice still has two inventory failures:

  • TestCodexIntegration.test_complete_file_inventory_sh
  • TestCodexIntegration.test_complete_file_inventory_ps

Both failures reproduce on a clean upstream/main worktree at 1b0556c, before this PR's changes. They expect bundled agent-context extension artifacts in the generated project, but the generated project does not include them. The focused tests added by this PR pass.

AI Assistance Disclosure

I used Codex/ChatGPT to inspect the integration paths, draft the implementation, and generate test coverage. I reviewed the code paths and validated the behavior locally with automated and manual checks.

@AustinZ21 AustinZ21 marked this pull request as ready for review June 14, 2026 21:07
@AustinZ21 AustinZ21 requested a review from mnriem as a code owner June 14, 2026 21:07
@AustinZ21

Copy link
Copy Markdown
Author

Hi maintainers, I've marked this PR as ready for review. It looks like the workflows are awaiting approval before the checks can run. Thanks for taking a look.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the bundled agent-context extension and related integration plumbing to support syncing the managed Spec Kit context block into multiple configured context files (via optional context_files), while keeping the legacy single context_file behavior as a fallback.

Changes:

  • Add context_files support across Python integration context upsert/remove, command/template rendering, and init-option updates (preserve non-empty lists across integration switches).
  • Update bash and PowerShell agent-context update scripts to iterate over all configured context files.
  • Expand tests to cover multi-file upsert/remove, opt-out behavior, and path-safety rejection cases.
Show a summary per file
File Description
tests/integrations/test_integration_codex.py Adds coverage ensuring Codex skill templates render multi-file context targets and ignore context_files when agent-context is disabled.
tests/extensions/test_extension_agent_context.py Adds tests for multi-file upsert/remove behavior, invalid-path rejection, and opt-out behavior with stale config.
src/specify_cli/integrations/hermes/init.py Switches template rendering to use the computed context-file display string.
src/specify_cli/integrations/generic/init.py Switches template rendering to use the computed context-file display string.
src/specify_cli/integrations/forge/init.py Switches template rendering to use the computed context-file display string.
src/specify_cli/integrations/copilot/init.py Switches template rendering to use the computed context-file display string.
src/specify_cli/integrations/base.py Implements context_files resolution + validation and updates context upsert/remove to operate over multiple files.
src/specify_cli/integrations/_helpers.py Ensures integration switching/clearing handles context_files preservation/clearing appropriately.
src/specify_cli/agents.py Updates SKILL placeholder resolution to prefer context_files when agent-context is enabled.
src/specify_cli/init.py Extends agent-context config load/update helpers to understand/preserve context_files.
extensions/agent-context/scripts/powershell/update-agent-context.ps1 Updates the updater to parse context_files and iterate updates over multiple files.
extensions/agent-context/scripts/bash/update-agent-context.sh Updates the updater to parse context_files and iterate updates over multiple files.
extensions/agent-context/README.md Documents multi-file configuration and opt-out behavior.
extensions/agent-context/commands/speckit.agent-context.update.md Updates command docs to include context_files semantics and validation notes.
extensions/agent-context/agent-context-config.yml Adds context_files to the default config template and clarifies semantics.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 15/15 changed files
  • Comments generated: 5

Comment thread src/specify_cli/integrations/base.py
Comment thread src/specify_cli/agents.py Outdated
Comment thread extensions/agent-context/scripts/bash/update-agent-context.sh
@AustinZ21

Copy link
Copy Markdown
Author

Addressed the Copilot review feedback in commit 10cbff3.

Summary:

  • Validated context_files / context_file before rendering skill placeholders.
  • Restored a string fallback for disabled agent-context template rendering.
  • Added bash and PowerShell resolved-path containment checks before context-file writes.
  • Added PowerShell backslash rejection for configured context files.
  • Added targeted tests for invalid config paths, backslashes, symlink/junction escape paths, and disabled-extension behavior.

Validation:

  • pytest tests/extensions/test_extension_agent_context.py tests/integrations/test_integration_codex.py::TestCodexInitFlow -q
  • git diff --check
  • bash -n extensions/agent-context/scripts/bash/update-agent-context.sh
  • PowerShell parser syntax check

Posted by @AustinZ21 with fixes prepared using Codex.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 15/15 changed files
  • Comments generated: 1

Comment thread src/specify_cli/agents.py
@mnriem

mnriem commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Please address Copilot feedback

@AustinZ21

Copy link
Copy Markdown
Author

Addressed the latest Copilot feedback and the Windows CI failures in commit 9458a6b.

Summary:

  • Preserved __CONTEXT_FILE__ rendering when agent-context is disabled by falling back to the extension config’s singular context_file.
  • Kept disabled-mode behavior from using stale context_files.
  • Updated the PowerShell updater tests so the subprocess uses the same Python environment as pytest, ensuring PyYAML is available in CI.

Validation:

  • pytest tests/extensions/test_extension_agent_context.py tests/integrations/test_integration_codex.py::TestCodexInitFlow -q
  • git diff --check
  • python -m compileall -q src/specify_cli/agents.py tests/extensions/test_extension_agent_context.py

The previous Windows failures were from commit 10cbff3; the fix is now pushed in 9458a6b.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 15/15 changed files
  • Comments generated: 4

Comment on lines +129 to +150
def _run_bash_agent_context_script(project_root: Path) -> subprocess.CompletedProcess:
script = EXT_DIR / "scripts" / "bash" / "update-agent-context.sh"
if os.name == "nt":
drive = project_root.drive.rstrip(":").lower()
root = project_root.as_posix()
root = f"/mnt/{drive}{root[2:]}" if drive else root
script_path = script.as_posix()
script_path = f"/mnt/{script.drive.rstrip(':').lower()}{script_path[2:]}"
command = f"cd {shlex_quote(root)} && {shlex_quote(script_path)}"
return subprocess.run(
[BASH, "-lc", command],
capture_output=True,
text=True,
timeout=30,
)
return subprocess.run(
[BASH, str(script)],
cwd=project_root,
capture_output=True,
text=True,
timeout=30,
)
Comment on lines +595 to +611
if os.name == "nt":
drive = tmp_path.drive.rstrip(":").lower()
root = tmp_path.as_posix()
root = f"/mnt/{drive}{root[2:]}" if drive else root
create_link = subprocess.run(
[
BASH,
"-lc",
f"ln -s {shlex_quote(root + '/outside')} "
f"{shlex_quote(root + '/project/link')}",
],
capture_output=True,
text=True,
timeout=30,
)
if create_link.returncode != 0:
pytest.skip(f"symlink unavailable: {create_link.stderr}")
Comment on lines +42 to +43
# Parse extension config once; emit three JSON lines:
# context files array, context_markers.start, context_markers.end
Comment on lines 1225 to 1229
raw = src_file.read_text(encoding="utf-8")
processed = self.process_template(
raw, self.key, script_type, arg_placeholder,
context_file=self.context_file or "",
context_file=self._context_file_display(project_root),
)

@mnriem mnriem left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address Copilot feedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants